#include "ftp_functions.h"

//TODO:

int is_relative(char *path) {
	return path[0] != '/';
}

char *build_absolute_path(connectionInfo conn, char *dir) {
	char *absolute, *ptr;
	if (!is_relative(dir))
		absolute = dir;
	else {
		absolute = (char *)malloc(sizeof(char) * (strlen(conn.pwd) + 1 + strlen(dir) + 1));
		if (absolute == NULL)
			return absolute;
		strcpy(absolute, conn.pwd);
		ptr = absolute;
		ptr  += strlen(conn.pwd);
		*ptr = '/';
		ptr++;
		strcpy(ptr, dir);
		free(dir);
	}

	return absolute;
}

int ftp_cd(char *data, int nbytes, int *sendNbytes, char **returnData, connectionInfo conn) {
	char *directory;
	int retval;

	directory = strtok(data, ARGUMENT_DELIMITER);
	directory = build_absolute_path(conn, directory);
	if (directory == NULL)
		return 1;

	free(conn.pwd);
	conn.pwd = directory;

	retval = chdir(directory);
	if (retval == 0) {
		*returnData = (char *)malloc(sizeof(char) * (strlen(FTP_RETURN_OK) + 1));
		sendNbytes = strlen(*returnData);
	} else {
		//TODO: differ between server fault, permissions, or anything else
		*returnData = (char *)malloc(sizeof(char) * (strlen(FTP_RETURN_ACCESS_DENIED) + 1));
		sendNbytes = strlen(*returnData);
	}
	return 0;
}

int ftp_lcd(char *data, int nbytes, int *sendNbytes, char **returnData, connectionInfo conn) {
	*returnData = (char *)malloc(sizeof(char) * (strlen(FTP_RETURN_OK) + 1));
	sendNbytes = strlen(*returnData);
	return 0;
}

int ftp_put(char *data, int nbytes, int *sendNbytes, char **returnData, connectionInfo conn) {
	if (conn.currentStatus == FTP_STATUS_WAITING_FOR_CONTENT &&
			conn.ofile == NULL)
		return 1; // We already should have received some of the file, handler should be open.
	if (conn.ofile == NULL) {
		char *filename = strtok(data, ARGUMENT_DELIMITER);
		filename = build_absolute_path(conn, filename);
		conn.ofile = fopen(filename, "w");
		if (conn.ofile == NULL) {
			return 1;
		}
		conn.currentStatus = FTP_STATUS_WAITING_FOR_CONTENT;
	} else {
		if (sendNbytes == 0) {
			fclose(conn.ofile);
			conn.ofile = NULL;
			conn.currentStatus = FTP_STATUS_WAITING_FOR_COMMEND;
		} else if (fwrite(data, sizeof(char), *sendNbytes, conn.ofile) != sendNbytes) {
			fclose(conn.ofile);
			//TODO: File cleanup
		}
	}
	*returnData = (char *)malloc(sizeof(char) * (strlen(FTP_RETURN_OK) + 1));
	sendNbytes = strlen(*returnData);
	return 0;
}

int ftp_get(char *data, int nbytes, int *sendNbytes, char **returnData, connectionInfo conn) {
	if (conn.currentStatus == FTP_STATUS_SENDING_CONTENT &&
			conn.ofile == NULL)
		return 1;
	if (conn.ofile == NULL) {
		char *filename = strtok(data, ARGUMENT_DELIMITER);
		filename = build_absolute_path(conn, filename);
		conn.ofile = fopen(filename, "r");
		if (conn.ofile == NULL) {
			return 1;
		}
		conn.currentStatus = FTP_STATUS_SENDING_CONTENT;
	} else {
		char *ptr;
		*sendNbytes = fread(*returnData + (strlen(FTP_RETURN_OK) + 1), sizeof(char), MAXDATA - strlen(FTP_RETURN_OK) - 1, conn.ofile);
		if (sendNbytes != (MAXDATA - strlen(FTP_RETURN_OK) - 1)) {
			conn.currentStatus = FTP_STATUS_WAITING_FOR_COMMEND;
			fclose(conn.ofile);
			conn.ofile = NULL;
		}
		strncpy(*returnData, FTP_RETURN_OK, strlen(FTP_RETURN_OK));
		ptr = *returnData;
		ptr += strlen(FTP_RETURN_OK);
		*ptr = ' ';
		*sendNbytes += strlen(FTP_RETURN_OK) + 1;
	}
	return 0;
}

int ftp_ls(char *data, int nbytes, int *sendNbytes, char **returnData, connectionInfo conn) {
	if (conn.currentStatus == FTP_STATUS_SENDING_CONTENT &&
			conn.odir == NULL)
		return 1;
	if (conn.odir == NULL) {
		char *dirname = strtok(data, ARGUMENT_DELIMITER);
		conn.odir = opendir(dirname);
		conn.ent = readdir(conn.odir);
		if (conn.odir == NULL) {
			return 1;
		}
		conn.currentStatus = FTP_STATUS_SENDING_CONTENT;
	} else {
		*sendNbytes = strlen(FTP_RETURN_OK) + 1;
		if (conn.ent == NULL) {
			*sendNbytes = strlen(FTP_RETURN_OK);
			strcpy(*returnData, FTP_RETURN_OK);
			closedir(conn.odir);
			conn.odir = NULL;
			conn.ent = NULL;
		} else {
			char *ptr;
			ptr = *returnData;
			strcpy(ptr, FTP_RETURN_OK);
			ptr += strlen(FTP_RETURN_OK);
			ptr++;
			*ptr = ' ';
			ptr++;
			while ((conn.ent != NULL) && (*sendNbytes + strlen(conn.ent->d_name) + 1 < MAXDATA)) {
				strcpy(ptr, conn.ent->d_name);
				*sendNbytes += 1 + strlen(conn.ent->d_name);
			}
		}
	}
	return 0;
}

int parse_command(char *data, int nbytes, int *sendNbytes, char **returnData, connectionInfo conn) {
	char *tok;
	tok = strtok(data, ARGUMENT_DELIMITER);
	nbytes -= (strlen(tok) + 1);

	if (strcmp(tok, FTP_COMMAND_CD) == 0 &&
			(conn.currentStatus == FTP_STATUS_WAITING_FOR_COMMEND)) {
		if (ftp_cd(data, nbytes, sendNbytes, returnData, conn)) {
			if (*returnData != NULL)
				free(*returnData);
			*returnData = (char *)malloc(sizeof(char) * (strlen(FTP_RETURN_SERVER_ERROR) + 1));
			strcpy(*returnData, FTP_RETURN_SERVER_ERROR);
			*sendNbytes = strlen(FTP_RETURN_SERVER_ERROR);
		}
	} else if (strcmp(tok, FTP_COMMAND_LCD) == 0 &&
			(conn.currentStatus == FTP_STATUS_WAITING_FOR_COMMEND)) {
		if (ftp_lcd(data, nbytes, sendNbytes, returnData, conn)) {
			if (*returnData != NULL)
				free(*returnData);
			*returnData = (char *)malloc(sizeof(char) * (strlen(FTP_RETURN_SERVER_ERROR) + 1));
			strcpy(*returnData, FTP_RETURN_SERVER_ERROR);
			*sendNbytes = strlen(FTP_RETURN_SERVER_ERROR);
		}
	} else if (strcmp(tok, FTP_COMMAND_LS) &&
			((conn.currentStatus == FTP_STATUS_WAITING_FOR_COMMEND) ||
					(conn.currentStatus == FTP_STATUS_SENDING_CONTENT))) {
		if (ftp_ls(data, nbytes, sendNbytes, returnData, conn)) {
			if (*returnData != NULL)
				free(*returnData);
			*returnData = (char *)malloc(sizeof(char) * (strlen(FTP_RETURN_SERVER_ERROR) + 1));
			strcpy(*returnData, FTP_RETURN_SERVER_ERROR);
			*sendNbytes = strlen(FTP_RETURN_SERVER_ERROR);
		}
	} else if (strcmp(tok, FTP_COMMAND_PUT) &&
			((conn.currentStatus == FTP_STATUS_SENDING_CONTENT) ||
					(conn.currentStatus == FTP_STATUS_WAITING_FOR_COMMEND))) {
		if (ftp_put(data, nbytes, sendNbytes, returnData, conn)) {
			if (*returnData != NULL)
				free(*returnData);
			*returnData = (char *)malloc(sizeof(char) * (strlen(FTP_RETURN_SERVER_ERROR) + 1));
			strcpy(*returnData, FTP_RETURN_SERVER_ERROR);
			*sendNbytes = strlen(FTP_RETURN_SERVER_ERROR);
		}
	} else if (strcmp(tok, FTP_COMMAND_GET) &&
			((conn.currentStatus == FTP_STATUS_WAITING_FOR_CONTENT) ||
					(conn.currentStatus == FTP_STATUS_WAITING_FOR_COMMEND))) {
		if (ftp_get(data, nbytes, sendNbytes, returnData, conn)) {
			if (*returnData != NULL)
				free(*returnData);
			*returnData = (char *)malloc(sizeof(char) * (strlen(FTP_RETURN_SERVER_ERROR) + 1));
			strcpy(*returnData, FTP_RETURN_SERVER_ERROR);
			*sendNbytes = strlen(FTP_RETURN_SERVER_ERROR);
		}
	} else {
		if (ftp_cd(data, nbytes, sendNbytes, returnData, conn)) {
			if (*returnData != NULL)
				free(*returnData);
			*returnData = (char *)malloc(sizeof(char) * (strlen(FTP_RETURN_COMMAND_NOT_FOUND) + 1));
			strcpy(*returnData, FTP_RETURN_COMMAND_NOT_FOUND);
			*sendNbytes = strlen(FTP_RETURN_COMMAND_NOT_FOUND);
		}
	}
	return 0;
}
